﻿using System;
using System.IO;
using System.Net;
using StockAccessors.Interdin.Model;
using Utils;
using Utils.Extensions;

namespace StockAccessors.Interdin.Engine
{
	internal class OrderExecutor : IDisposable
	{
		#region Attributes

		private readonly DetailsLinkManager _linkManager;

		#endregion

		#region Properties

		public CookieContainer Cookies { get; set; }
		public bool IsSimulationEnabled { get; set; }

		#endregion

		#region Public Methods

		public OrderExecutor(FuturesPageReader futuresPageReader, CfdPageReader cfdPageReader)
		{
			_linkManager = new DetailsLinkManager(futuresPageReader, cfdPageReader);
		}

		public OperationExecutionResult ExecuteMarketOrder(string symbol, int volume, bool buySell)
		{
			if (!_linkManager.Details.ContainsKey(symbol))
			{
				throw new OrderExecutionException("The requested symbol " + symbol + " does not exist on the list of symbols");
			}
			else
			{
				if (this.IsSimulationEnabled)
				{
					return new OperationExecutionResult(new Random().Next(1, 100), DateTime.Now);
				}
				else
				{
					DetailsLink detailsLink = _linkManager.Details[symbol];
					DetailsPageReader detailsPageReader = new DetailsPageReader() { Cookies = this.Cookies };
					DetailsPage detailsPage = detailsPageReader.ReadPage(detailsLink);
					OrderExecutionLink orderExecutionLink = new OrderExecutionLink(detailsPage.ContractNumber, detailsPage.IP, detailsPage.Year, detailsPage.Month, detailsPage.St, buySell, volume, detailsPage.RKNR, detailsPage.A, detailsPage.IO, detailsPage.DD);
					
					return this.ExecuteMarketOrder(orderExecutionLink);
				}
			}
		}

		public void ExecuteLimitedOrder(string symbol, int volume, bool buySell, decimal value)
		{
			if (!_linkManager.Details.ContainsKey(symbol))
			{
				throw new OrderExecutionException("The requested symbol " + symbol + " does not exist on the list of symbols");
			}
			else
			{
				if (!this.IsSimulationEnabled)
				{
					DetailsLink detailsLink = _linkManager.Details[symbol];
					DetailsPageReader detailsPageReader = new DetailsPageReader() { Cookies = this.Cookies };
					DetailsPage detailsPage = detailsPageReader.ReadPage(detailsLink);
					OrderExecutionLink orderExecutionLink = new OrderExecutionLink(detailsPage.ContractNumber, detailsPage.IP, detailsPage.Year, detailsPage.Month, detailsPage.St, buySell, volume, value, detailsPage.RKNR, detailsPage.A, detailsPage.IO, detailsPage.DD);
					this.SendLimitedOrder(orderExecutionLink);
				}
			}
		}

		#endregion

		#region Private Methods

		private OperationExecutionResult ExecuteMarketOrder(OrderExecutionLink orderExecutionLink)
		{
			using (HttpWebResponse response = HttpWebRequest2.SendPOSTRequest(orderExecutionLink.GetMarketLinkParameters(), OrderExecutionLink.Url, Properties.Settings.Default.UserAgent, this.Cookies, Properties.Settings.Default.ReadTimeout))
			{
				return this.ProcessResponse(response);
			}
		}

		private void SendLimitedOrder(OrderExecutionLink orderExecutionLink)
		{
			using (HttpWebResponse response = HttpWebRequest2.SendPOSTRequest(orderExecutionLink.GetLimitedLinkParameters(), OrderExecutionLink.Url, Properties.Settings.Default.UserAgent, this.Cookies, Properties.Settings.Default.ReadTimeout))
			{
				this.ProcessResponse(response);
			}
		}

		private OperationExecutionResult ProcessResponse(HttpWebResponse response)
		{
			string subResponse2Url = this.ValidateSubResponse1(response);
			using (HttpWebResponse subResponse2 = HttpWebRequest2.SendGETRequest(subResponse2Url, Properties.Settings.Default.UserAgent, this.Cookies, Properties.Settings.Default.ReadTimeout))
			{
				string subResponse3Url = this.ValidateSubResponse2(subResponse2);
				using (HttpWebResponse subResponse3 = HttpWebRequest2.SendGETRequest(subResponse3Url, Properties.Settings.Default.UserAgent, this.Cookies, Properties.Settings.Default.ReadTimeout))
				{
					string responseText;
					using (Stream webStream = response.GetResponseStream())
					{
						responseText = webStream.ReadTextResponse(response.CharacterSet);
					}

					this.ValidateSubResponse3(responseText, subResponse3.ResponseUri.ToString());
					return this.ExtractOrderResult(responseText, subResponse3.ResponseUri.ToString());
				}
			}
		}

		private string ValidateSubResponse1(HttpWebResponse response)
		{
			string responseText;
			using(Stream webStream = response.GetResponseStream())
			{
				responseText = webStream.ReadTextResponse(response.CharacterSet);
			}

			int startIndex = responseText.IndexOf("\"", StringComparison.InvariantCultureIgnoreCase) + 1;
			int endIndex = responseText.IndexOf("\"", startIndex + 1, StringComparison.InvariantCultureIgnoreCase);
			string newUrl = responseText.Substring(startIndex, endIndex - startIndex);

			if (!newUrl.StartsWith(@"PaginaIntermedia.asp?", StringComparison.InvariantCultureIgnoreCase)) 
			{
				OrderExecutor.ThrowOrderExecutionException("The subresponse1 redirects us to an unknow address. Url: " + newUrl, responseText, response.ResponseUri.ToString());
			}

			if (!newUrl.ToUpperInvariant().Contains(@"&ME="))
			{
				OrderExecutor.ThrowOrderExecutionException("The subresponse1 url does not contains the ME parameter. Url: " + newUrl, responseText, response.ResponseUri.ToString());
			}
			else if (!newUrl.ToUpperInvariant().Contains(@"&ME=&"))
			{
				startIndex = newUrl.IndexOf(@"&ME=", StringComparison.InvariantCultureIgnoreCase);
				endIndex = newUrl.IndexOf("&", startIndex, StringComparison.InvariantCultureIgnoreCase);
				string message = newUrl.Substring(startIndex, endIndex - startIndex);

				OrderExecutor.ThrowOrderExecutionException("The subresponse1 url contains and error message. Message: " + message, responseText, response.ResponseUri.ToString());
			}

			return "https://www.interdinfuturos.com/Broker/Ops/" + newUrl;
		}


		private string ValidateSubResponse2(HttpWebResponse response)
		{
			string responseText;
			using (Stream webStream = response.GetResponseStream())
			{
				responseText = webStream.ReadTextResponse(response.CharacterSet);
			}

			int startIndex = responseText.IndexOf("'", StringComparison.InvariantCultureIgnoreCase) + 1;
			int endIndex = responseText.IndexOf("'", startIndex, StringComparison.InvariantCultureIgnoreCase);
			string newUrl = responseText.Substring(startIndex, endIndex - startIndex);

			if (!newUrl.StartsWith(@"FichaFinalJS.asp?"))
			{
				OrderExecutor.ThrowOrderExecutionException("The subresponse2 redirects us to an unknow address. Url: " + newUrl, responseText, response.ResponseUri.ToString());
			}

			if (!newUrl.Contains(@"&ME="))
			{
				OrderExecutor.ThrowOrderExecutionException("The subresponse2 url does not contains the ME parameter. Url: " + newUrl, responseText, response.ResponseUri.ToString());
			}
			else if (!newUrl.Contains(@"&ME=&"))
			{
				startIndex = newUrl.IndexOf(@"&ME=", StringComparison.InvariantCultureIgnoreCase);
				endIndex = newUrl.IndexOf("&", startIndex, StringComparison.InvariantCultureIgnoreCase);
				string message = newUrl.Substring(startIndex, endIndex - startIndex);

				OrderExecutor.ThrowOrderExecutionException("The subresponse2 url contains and error message. Message: " + message, responseText, response.ResponseUri.ToString());
			}

			return "https://www.interdinfuturos.com/Broker/Ops/" + newUrl;
		}

		private void ValidateSubResponse3(string responseText, string responseUri)
		{
			int startIndex = responseText.IndexOf(@"<form name='Volver' action='FichaContratoJS.asp' method='GET'>", StringComparison.InvariantCulture);
			int endIndex = responseText.IndexOf(@"</form>", startIndex, StringComparison.InvariantCultureIgnoreCase) + @"</form>".Length;

			if (startIndex == -1 || endIndex == -1)
			{
				OrderExecutor.ThrowOrderExecutionException("The final response message is not the expected one. Cannot find <form name='Volver' action='FichaContratoJS.asp' method='GET'> or </form> in the response", responseText, responseUri);
			}

			string messageContainer = responseText.Substring(startIndex, endIndex - startIndex);

			startIndex = messageContainer.IndexOf(@"<td align=""center"" class=""textonegro"">", StringComparison.InvariantCultureIgnoreCase) + @"<td align=""center"" class=""textonegro"">".Length;
			endIndex = messageContainer.IndexOf(@"</td>", startIndex, StringComparison.InvariantCultureIgnoreCase);

			if (startIndex == -1 || endIndex == -1)
			{
				OrderExecutor.ThrowOrderExecutionException("The final response message is not the expected one. Cannot find <td align=\"center\" class=\"textonegro\"> or </td> in the response", responseText, responseUri);
			}

			string message = messageContainer.Substring(startIndex, endIndex - startIndex);

			if (message != "Su orden ha sido enviada al mercado.")
			{
				OrderExecutor.ThrowOrderExecutionException("The final response message is not the expected one. Message: " + message, responseText, responseUri);
			}
		}

		private OperationExecutionResult ExtractOrderResult(string responseText, string responseUri)
		{
			int startIndex = responseText.IndexOf("yellow", StringComparison.InvariantCultureIgnoreCase);
			startIndex = responseText.IndexOf("<span", startIndex, StringComparison.InvariantCultureIgnoreCase);
			startIndex = responseText.IndexOf(">", startIndex, StringComparison.InvariantCultureIgnoreCase);
			int endIndex = responseText.IndexOf("</span>", startIndex, StringComparison.InvariantCultureIgnoreCase);

			string date = responseText.Substring(startIndex, endIndex - startIndex);

			startIndex = responseText.IndexOf("yellow", endIndex, StringComparison.InvariantCultureIgnoreCase);
			startIndex = responseText.IndexOf("<td", startIndex, StringComparison.InvariantCultureIgnoreCase);
			startIndex = responseText.IndexOf(">", startIndex, StringComparison.InvariantCultureIgnoreCase);
			endIndex = responseText.IndexOf("</td>", startIndex, StringComparison.InvariantCultureIgnoreCase);

			string message = responseText.Substring(startIndex, endIndex - startIndex);
			string value = System.Text.RegularExpressions.Regex.Match(message, @"\sa\s.*?\s").ToString().Substring(3); // Remove the " a " before XXXX

			return new OperationExecutionResult(Decimal.Parse(value), DateTime.Parse(date));
		}

		private static void ThrowOrderExecutionException(string message, string responseText, string responseUri)
		{
			OrderExecutionException ex = new OrderExecutionException(message);
			ex.Data.Add("responseText", responseText);
			ex.Data.Add("responseUri", responseUri);
			throw ex;	
		}

		#endregion

		#region IDisposable

		protected bool Disposed { get; private set; }

		private void Dispose(bool disposing)
		{
			if (!this.Disposed)
			{
				this.InternalDispose(disposing);
			}

			this.Disposed = true;
		}

		protected virtual void InternalDispose(bool disposing)
		{
			if (disposing)
			{
				_linkManager.Dispose();
			}

			// Dispose here only unmanaged objects 
			// Don’t use managed objects here because maybe 
			// they have been finalized already
		}

		public void Dispose()
		{
			this.Dispose(true);
			GC.SuppressFinalize(this);
		}

		~OrderExecutor()
		{
			this.Dispose(false);
		}

		#endregion
	}
}
